#ifndef __SHM_HPP__
#define __SHM_HPP__

#include <iostream>
#include <string>
#include <vector>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <cstring>
#include <sys/shm.h>

#define Creater 1
#define User 2
const std::string gpathname = "/home/shh/Linus_study/communication/shm";
const int gproj_id = 0x66;
const int gShmSize = 4096;

class Shm
{
    key_t GetCommKey()
    {
        int key = ftok(_pathname.c_str(), _proj_id);
        if (key < 0)
        {
            perror("ftok");
        }
        return key;
    }
    int GetShmHelper(int key, int size, int who)
    {
        int shmid = shmget(key, size, who);
        if (shmid < 0)
        {
            perror("shmget");
        }
        return shmid;
    }

    bool GetShmUseCreate()
    {
        _shmid = GetShmHelper(_key, gShmSize, IPC_CREAT | IPC_EXCL | 0666); // 在创建共享内存是设置正确的权限
        if (_shmid > 0)
        {
            std::cout << "Create Shm done" << std::endl;
            return true;
        }
        return false;
    }
    bool GetShmForUse()
    {
        _shmid = GetShmHelper(_key, gShmSize, IPC_CREAT | 0666);
        if (_shmid > 0)
        {
            std::cout << "Get Shm done" << std::endl;
            return true;
        }
        return false;
    }

    std::string RoleToString(int _who)
    {
        if (_who == Creater)
            return "Creater";
        else if (_who == User)
            return "User";
        else
            return "None";
    }

    // 挂接共享内存shm
    void *AttachShm()
    {
        if (_addr != nullptr)
            DetachShm();
        void *addr = shmat(_shmid, nullptr, 0);
        if (addr == nullptr)
        {
            perror("shmat");
        }
        std::cout <<  RoleToString(_who) << " attach shm.." << std::endl;
        return addr;
    }
    void DetachShm()
    {
        if(_addr == nullptr) return;
        int res = shmdt(_addr);
        if (res < 0)
        {
            perror("shmat");
        }
        std::cout << "who:" << RoleToString(_who) << " detach" << std::endl;
    }

public:
    Shm(const std::string pathname, int proj_id, int who)
        : _pathname(pathname), _proj_id(proj_id), _who(who), _addr(nullptr)
    {
        _key = GetCommKey();
        if (who == Creater)
            GetShmUseCreate();
        else if (who == User)
            GetShmForUse();
        _addr = AttachShm();
    }

    ~Shm()
    {
        if (_who == Creater)
        {
            int res = shmctl(_shmid, IPC_RMID, nullptr);
            if (res < 0)
            {
                perror("shmctl");
            }
        }
        DetachShm();
    }

    void Zero()
    {
        if(_addr != nullptr)
        {
            memset(_addr,0,gShmSize);
        }
    }
    void * Addr()
    {
        return _addr;
    }
private:
    key_t _key;
    int _shmid;

    std::string _pathname;
    int _proj_id;
    int _who;
    void *_addr;
};

#endif
